package cs

import (
	"fmt"
	"github.com/gogo/protobuf/proto"
	"github.com/sniperHW/fly/core/buffer"
	"github.com/sniperHW/fly/core/compress"
	"github.com/sniperHW/fly/errcode"
	"github.com/sniperHW/fly/net/pb"
	flyproto "github.com/sniperHW/fly/proto"
)

func isPow2(size int) bool {
	return (size & (size - 1)) == 0
}

func sizeofPow2(size int) int {
	if isPow2(size) {
		return size
	}
	size = size - 1
	size = size | (size >> 1)
	size = size | (size >> 2)
	size = size | (size >> 4)
	size = size | (size >> 8)
	size = size | (size >> 16)
	return size + 1
}

type InboundProcessorBase struct {
	Buffer     []byte
	w          int
	r          int
	BaseUnpack func([]byte, int, int) (interface{}, int, error)
}

type InboundProcessor struct {
	*InboundProcessorBase
	unCompressor compress.UnCompressorI
	pbSpace      *pb.Namespace
}

func (this *InboundProcessor) unpack(b []byte, r int, w int) (ret interface{}, packetSize int, err error) {
	unpackSize := w - r
	if unpackSize > minSize {
		var msg proto.Message

		reader := buffer.NewReader(b[r : r+unpackSize])
		payload := int(reader.GetUint32())

		if payload == 0 {
			err = fmt.Errorf("zero payload")
			return
		}

		if payload+SizeLen > MaxPacketSize {
			err = fmt.Errorf("large packet %d", payload+SizeLen)
			return
		}

		totalSize := payload + SizeLen

		packetSize = totalSize

		if totalSize <= unpackSize {

			//read head
			var head CommonHead

			head.Seqno = reader.GetInt64()

			errCode := reader.GetInt16()
			if errCode == 0 {
				head.Err = nil
			} else {
				errDesc := ""
				sizeOfErrDesc := int(reader.GetUint16())
				if sizeOfErrDesc > 0 {
					errDesc = reader.GetString(sizeOfErrDesc)
				}
				head.Err = errcode.New(errCode, errDesc)
			}

			sizeOfUniKey := int(reader.GetUint16())

			if sizeOfUniKey > 0 {
				head.UniKey = reader.GetString(sizeOfUniKey)
			}

			head.Timeout = reader.GetUint32()

			flag := reader.GetByte()

			cmd := reader.GetUint16()

			pbsize := int(reader.GetInt32())

			buff := reader.GetBytes(pbsize)

			if flag == byte(1) {
				if nil == this.unCompressor {
					err = fmt.Errorf("invaild compress packet")
					return
				}

				if buff, err = this.unCompressor.UnCompress(buff); err != nil {
					return
				}
			}

			if msg, err = this.pbSpace.Unmarshal(uint32(cmd), buff); err != nil {
				return
			}

			ret = NewMessageWithCmd(flyproto.CmdType(cmd), head, msg)
		}
	}
	return
}
